package vip.manda.framework.web.http;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.json.JSONUtil;
import vip.manda.framework.bean.BeanFactory;
import vip.manda.framework.util.Requester;
import vip.manda.framework.util.Triad;
import vip.manda.framework.log.Log;
import vip.manda.framework.log.LogFactory;
import vip.manda.framework.web.BaseController;
import vip.manda.framework.web.View;
import vip.manda.framework.web.WebFunction;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

/**
 * @author hongda.li 2022-04-11 16:38
 */
public class CenterServlet extends HttpServlet {
    private static final Log log = LogFactory.getLog();

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        log.info("CenterServlet start working");
        Triad<String, Class<?>, Method> controller = WebFunction.getController(Requester.getUri(req.getRequestURI()));
        if (controller == null){
            log.debug("404 error : Request mapping don't match![{}]", req.getRequestURI());
            resp.sendError(404, "404 error : Request mapping don't match!");
            return;
        }
        log.info("Transfer request[{}][{}]", controller.getY().getName(), controller.getZ().getName());
        HttpSession session = req.getSession();
        session.setMaxInactiveInterval(24 * 60);
        Object bean = BeanFactory.getContainer().getBean(controller.getY());
        if (bean instanceof BaseController) {
            ReflectUtil.setFieldValue(bean, "params", req.getParameterMap());
            ReflectUtil.setFieldValue(bean, "request", req);
            ReflectUtil.setFieldValue(bean, "response", resp);
            ReflectUtil.setFieldValue(bean, "session", session);
            ReflectUtil.setFieldValue(bean, "jsonStr", handleJsonParameter(req).toString());
            ReflectUtil.setFieldValue(bean, "log", LogFactory.getLog(controller.getY()));
            try {
                Object result = ReflectUtil.invoke(bean, controller.getZ());
                writeBackData(result, resp);
            } catch (Exception e) {
                log.error("Controller execute error[{}][{}]", controller.getY().getName(), controller.getZ().getName());
                resp.sendError(500, "500 error : Controller execute error");
                e.printStackTrace();
            }
        } else {
            try {
                Object result = new ParamBinder(controller, req, resp).invoke();
                writeBackData(result, resp);
            } catch (Exception e) {
                log.error("Controller execute error[{}][{}]", controller.getY().getName(), controller.getZ().getName());
                resp.sendError(500, "500 error : Controller execute error");
                e.printStackTrace();
            }
        }
    }

    private StringBuilder handleJsonParameter(HttpServletRequest request) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8));
        String line;
        StringBuilder builder = new StringBuilder();
        while ((line = br.readLine()) != null) {
            builder.append(line);
        }
        return builder;
    }

    private void writeBackData(Object result, HttpServletResponse response) throws IOException {
        if (result == null) {
            return;
        }
        if (result instanceof String) {
            log.info("Write html data[{}]", result);
            response.setContentType(StaticResourceHandler.CONTENT_TYPES.get(".html"));
            response.getWriter().write((String) result);
        } else if (result instanceof View) {
            String name = ((View) result).getName();
            log.info("Write freemarker data[{}]", name);
            response.setContentType(StaticResourceHandler.CONTENT_TYPES.get(".html"));
            View.ENGINE.getTemplate(name).render(((View) result).getParams(), response.getWriter());
        } else if (result instanceof File) {
            File file = (File) result;
            FileInputStream fis = new FileInputStream(file);
            String filename = URLEncoder.encode(file.getName(), "UTF-8");
            log.info("Write file data[{}]", filename);
            byte[] b = new byte[fis.available()];
            //noinspection ResultOfMethodCallIgnored
            fis.read(b);
            response.setHeader("Content-Disposition", "attachment; filename=" + filename);
            ServletOutputStream out = response.getOutputStream();
            out.write(b);
            out.flush();
            out.close();
        } else {
            String jsonStr = JSONUtil.toJsonStr(result);
            response.setContentType(StaticResourceHandler.CONTENT_TYPES.get(".json"));
            log.info("Write json data[{}]", jsonStr);
            PrintWriter writer = response.getWriter();
            writer.write(jsonStr);
        }
    }
}
